package zaplog

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

type LogConfig struct {
	filename       string // 日志文件
	LogDir         string
	WithMaxAge     time.Duration // 日志保留时间
	IsPrintConsole bool          // 开发模式会输出日志到标准输出
	FileSplitTime  time.Duration // 日志文件拆分时间间隔 time.Hour*24
	Level          string
	// Buffered Writer
	BufferedSize          int           // 缓冲区大小，默认为 16k（与 slog 保持相对一致），设置为 0 则为同步阻塞输出日志，否则为异步输出，缓冲区写满时输出一次
	BufferedFlushInterval time.Duration // Flush 缓冲区时间间隔，默认为 1s
}

var (
	logLogger *zap.SugaredLogger
	logWriter io.Writer = os.Stdout
)

func init() {
	// 设置一些基本日志格式
	encoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
		MessageKey:  "msg",
		LevelKey:    "level",
		EncodeLevel: zapcore.CapitalLevelEncoder,
		TimeKey:     "ts",
		EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
			enc.AppendString(t.Format("[2006-01-02 15:04:05.000 MST]"))
		},
		CallerKey:    "file",
		EncodeCaller: zapcore.ShortCallerEncoder,
		EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) {
			enc.AppendInt64(int64(d) / 1000000)
		},
		ConsoleSeparator: " ",
	})
	// 实现两个判断日志等级的interface
	//logLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
	//	return lvl >= zapcore.DebugLevel
	//})
	//errorLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
	//	return lvl >= zapcore.ErrorLevel
	//})
	// 获取 info、error日志文件的io.Writer 抽象 getWriter() 在下方实现
	//errorWriter := getWriter("./logs/demo_error.log")
	cores := []zapcore.Core{
		zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), zapcore.DebugLevel),
	}
	// 最后创建具体的Logger
	core := zapcore.NewTee(cores...)
	log := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)) // 需要传入 zap.AddCaller() 才会显示打日志点的文件名和行数, 有点小坑
	logLogger = log.Sugar()
}
func Init(cfg *LogConfig) *zap.Logger {
	os.MkdirAll(cfg.LogDir, os.ModePerm)
	// 设置一些基本日志格式
	encoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
		MessageKey:  "msg",
		LevelKey:    "level",
		EncodeLevel: zapcore.CapitalLevelEncoder,
		TimeKey:     "ts",
		EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
			enc.AppendString(t.Format("[2006-01-02 15:04:05.000 MST]"))
		},
		CallerKey:    "file",
		EncodeCaller: zapcore.ShortCallerEncoder,
		EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) {
			enc.AppendInt64(int64(d) / 1000000)
		},
		ConsoleSeparator: " ",
	})
	hostName, _ := os.Hostname()
	isDaySplit := false
	logTail := ""
	if cfg.FileSplitTime > time.Hour {
		logTail = "%Y%m%d"
	} else if cfg.FileSplitTime > time.Minute {
		isDaySplit = true
		logTail = "%Y%m%d_%H"
	} else {
		isDaySplit = true
		logTail = "%Y%m%d_%H%M"
	}
	if hostName != "" {
		logTail = logTail + "." + hostName
	}

	if isDaySplit {
		cfg.filename = filepath.Join(cfg.LogDir, "%v", GetExeName()+".%v.log")
		cfg.filename = fmt.Sprintf(cfg.filename, "%Y%m%d", logTail)
	} else {
		cfg.filename = filepath.Join(cfg.LogDir, GetExeName()+".%v.log")
		cfg.filename = fmt.Sprintf(cfg.filename, logTail)
	}
	var level zapcore.Level
	if cfg.Level == "" {
		level = zapcore.DebugLevel
	} else {
		if err := level.Set(cfg.Level); err != nil {
			level = zapcore.DebugLevel
		}
	}

	// 获取 info、error日志文件的io.Writer 抽象 getWriter() 在下方实现
	logWriter = getWriter(cfg)
	cores := []zapcore.Core{
		//zapcore.NewCore(encoder, zapcore.AddSync(logWriter), level),
	}
	if cfg.BufferedSize < 1 {
		cfg.BufferedSize = 16 * 1024 // 默认 16k
	}
	if cfg.BufferedFlushInterval < time.Second {
		cfg.BufferedFlushInterval = time.Second // 默认 1s
	}
	if cfg.BufferedSize > 0 {
		bws := zapcore.BufferedWriteSyncer{
			WS:            zapcore.AddSync(logWriter),
			Size:          cfg.BufferedSize,
			FlushInterval: cfg.BufferedFlushInterval,
		}
		cores = append(cores, zapcore.NewCore(encoder, &bws, level))
	} else {
		cores = append(cores, zapcore.NewCore(encoder, zapcore.AddSync(logWriter), level))
	}

	if cfg.IsPrintConsole {
		cores = append(cores, zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), level))
	}
	// 最后创建具体的Logger
	core := zapcore.NewTee(cores...)
	log := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)) // 需要传入 zap.AddCaller() 才会显示打日志点的文件名和行数, 有点小坑
	logLogger = log.Sugar()
	return log
}
func GetLogWriter() io.Writer {
	return logWriter
}
func getWriter(cfg *LogConfig) io.Writer {
	hook, err := rotatelogs.New(
		cfg.filename, // 没有使用go风格反人类的format格式
		//rotatelogs.WithLinkName(filename),
		rotatelogs.WithMaxAge(cfg.WithMaxAge),
		rotatelogs.WithRotationTime(cfg.FileSplitTime),
	)
	if err != nil {
		panic(err)
	}
	return hook
}
func Debug(args ...interface{}) {
	logLogger.Debug(args...)
}
func Debugf(template string, args ...interface{}) {
	logLogger.Debugf(template, args...)
}
func Info(args ...interface{}) {
	logLogger.Info(args...)
}
func Infof(template string, args ...interface{}) {
	logLogger.Infof(template, args...)
}
func Warn(args ...interface{}) {
	logLogger.Warn(args...)
}
func Warnf(template string, args ...interface{}) {
	logLogger.Warnf(template, args...)
}
func Error(args ...interface{}) {
	logLogger.Error(args...)
}
func Errorf(template string, args ...interface{}) {
	logLogger.Errorf(template, args...)
}
func Fatal(args ...interface{}) {
	logLogger.Fatal(args...)
}
func Fatalf(template string, args ...interface{}) {
	logLogger.Fatalf(template, args...)
}

//	func DPanic(args ...interface{}) {
//		logLogger.DPanic(args...)
//	}
//
//	func DPanicf(template string, args ...interface{}) {
//		logLogger.DPanicf(template, args...)
//	}
//
//	func Panic(args ...interface{}) {
//		logLogger.Panic(args...)
//	}
//
//	func Panicf(template string, args ...interface{}) {
//		logLogger.Panicf(template, args...)
//	}
//
//	func Fatal(args ...interface{}) {
//		logLogger.Fatal(args...)
//	}
//
//	func Fatalf(template string, args ...interface{}) {
//		logLogger.Fatalf(template, args...)
//	}
func GetExeName() string {
	pathName := filepath.Base(os.Args[0])
	return strings.ReplaceAll(pathName, filepath.Ext(pathName), "")
}
